using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace System.Windows.Forms
{
	/// 
	/// Summary description for DraggableTabPage.
	/// 
	public class DraggableTabControl : TabControl
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		public event CancelEventHandler TabClosing;
		private TabPage closingTab;
		public TabPage ClosingTab
		{
			get { return closingTab; }
		}
		protected void OnTabClosing()
		{
			CancelEventArgs e = new CancelEventArgs();
			if(TabClosing != null)
			{
				try {
					TabClosing(this, e);
				} catch(Exception) {
					throw;
				}
			}
			if(!e.Cancel)
				CloseTab();
		}

		public DraggableTabControl() : base()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			DrawMode = TabDrawMode.OwnerDrawFixed;
			Padding = new Point(17,3);
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if(disposing)
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}
		#endregion

		protected override void OnDragOver(System.Windows.Forms.DragEventArgs e)
        {
            base.OnDragOver(e);

            Point pt = new Point(e.X, e.Y);
            //We need client coordinates.
            pt = PointToClient(pt);

            //Get the tab we are hovering over.
            TabPage hover_tab = GetTabPageByPoint(pt);

            //Make sure we are on a tab.
            if(hover_tab != null)
            {
                //Make sure there is a TabPage being dragged.
                if(e.Data.GetDataPresent(typeof(TabPage)))
                { 
                    e.Effect = DragDropEffects.Move; 
                    TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage));
                    int item_drag_index = FindIndex(drag_tab);
                    int drop_location_index= FindIndex(hover_tab); 

                    //Don't do anything if we are hovering over ourself.
                    if(item_drag_index != drop_location_index)  {

                        ArrayList pages = new ArrayList(); 
                        //Put all tab pages into an array.
                        for(int i = 0; i < TabCount; i++)
                        {
                            //Except the one we are dragging.
                            if(i != item_drag_index)
                                pages.Add(TabPages[i]);
                        }

                        //Now put the one we are dragging it at the proper location.
                        pages.Insert(drop_location_index, drag_tab);

                        //Make them all go away for a nanosec.
                        TabPages.Clear();

                        //Add them all back in.
                        TabPages.AddRange((TabPage[])pages.ToArray(typeof(TabPage)));

                        //Make sure the drag tab is selected.
                        SelectedTab = drag_tab;
                    } else {
						//We're selecting this tab, not dragging it
						SelectedTab = drag_tab;
                    }
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }
		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);
			for(int i = 0; i < TabCount; i++)
			{
				if(new Rectangle(GetTabRect(i).Right-15, GetTabRect(i).Top+7, 5, 5).Contains(e.Location))
				{
					closingTab = TabPages[i];
					OnTabClosing();
					closingTab = null;
				}
			}
			TabPage tp = GetTabPageByPoint(e.Location);
			if(tp != null)
				DoDragDrop(tp, DragDropEffects.All);
		}
		protected override void OnDrawItem(DrawItemEventArgs e)
		{
			Pen pen = new Pen(Brushes.Black, 1f);
			int x = e.Bounds.Right-15, y = e.Bounds.Top+7;
			e.Graphics.DrawLine(pen, new Point(x, y), new Point(x+5, y+5));
			e.Graphics.DrawLine(pen, new Point(x+5, y), new Point(x, y+5));
			e.Graphics.DrawString(TabPages[e.Index].Text, Font, Brushes.Black, new PointF(e.Bounds.X+4, e.Bounds.Y+2));
			base.OnDrawItem(e);
		}

		private void CloseTab()
		{
			TabPages.Remove(ClosingTab);
		}
		/// <summary>
		/// Finds the TabPage whose tab is contains the given point.
		/// </summary>
		/// <param name="pt">The point (given in client coordinates) to look for a TabPage.</param>
		/// <returns>The TabPage whose tab is at the given point (null if there isn't one).</returns>
		public TabPage GetTabPageByPoint(Point pt)
		{
			TabPage tp = null;
			for(int i = 0; i < TabCount; i++)
			{
				if(GetTabRect(i).Contains(pt))
				{
					tp = TabPages[i];
					break;
				}
			}
			return tp;
		}

		/// <summary>
		/// Loops over all the TabPages to find the index of the given TabPage.
		/// </summary>
		/// <param name="page">The TabPage we want the index for.</param>
		/// <returns>The index of the given TabPage(-1 if it isn't found.)</returns>
		public int FindIndex(TabPage page)
		{
			int j = -1;
			for(int i = 0; i < TabCount; i++)
			{
				if(TabPages[i] == page)
				{
					j = i;
					break;
				}
			}
			return j;
		}
		/// <summary>
		/// Finds the TabPage that has a given control on it
		/// </summary>
		/// <param name="control">The control to search for</param>
		/// <returns>The TabPage who owns the control, or null if not found</returns>
		public TabPage GetTabPageByControl(Control control)
		{
			TabPage tp = null;
			foreach(TabPage page in TabPages)
			{
				if(page.Controls[0].Controls.Find(control.Name, true).Length > 0)
				{
					tp = page;
					break;
				}
			}
			return tp;
		}
	}
}
